package main

import (
	. "config"
	"database/sql"
	"fmt"
	"github.com/gin-gonic/gin"
	"myerror"
	"path"
	"strconv"
	"users"
)

func Index(ctx *gin.Context) {
	ctx.HTML(200, "main/index", nil)
}

func NetDisk(ctx *gin.Context) {
	var rows *sql.Rows
	defer func() {
		if rows != nil {
			err := rows.Close()
			if err != nil {
				myerror.Raise500(ctx, err)
			}
		}
	}()
	dirId, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	userId, err := users.GetUser(ctx)
	if err != nil {
		return
	}
	rows, err = DB.Query("select id,file_name,parent_dir,type,owner,size from file where id=? and owner=?", dirId, userId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if !rows.Next() {
		myerror.Raise404(ctx, fmt.Errorf("无此文件夹或此文件夹不属于该用户"))
		return
	}
	var thisDir FileType
	// 这里为了防止root文件夹ParentDir为null
	// 所以使用NullString
	// 不用NullInt64使因为其他地方使用的都是uint64
	// 防止大小不够
	var s sql.NullString
	err = rows.Scan(&thisDir.Id, &thisDir.FileName, &s, &thisDir.Type, &thisDir.Owner, &thisDir.Size)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if s.Valid {
		thisDir.ParentDir, err = strconv.ParseUint(s.String, 10, 64)
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
	} else {
		thisDir.ParentDir = 0
	}
	err = rows.Close()
	if err != nil {
		myerror.Raise500(ctx, err)
	}
	if thisDir.Type == DIR || thisDir.Type == ROOT {
		rows, err = DB.Query("select id,file_name,parent_dir,type,owner,size from file where parent_dir=? and owner=?", dirId, userId)
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		var sonFiles []FileType
		for rows.Next() {
			var f FileType
			err = rows.Scan(&f.Id, &f.FileName, &f.ParentDir, &f.Type, &f.Owner, &f.Size)
			if err != nil {
				myerror.Raise500(ctx, err)
				return
			}
			sonFiles = append(sonFiles, f)
		}
		warning := ctx.Query("warning")
		ctx.HTML(200, "main/netdisk", gin.H{
			"sonFiles": sonFiles,
			"thisDir":  thisDir,
			"warning":  warning,
		})
	} else {
		dirId, err = strconv.ParseUint(ctx.Param("id"), 10, 64)
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		rows, err = DB.Query("select data,type,file_name from file where id=?", dirId)
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		rows.Next()
		data := make([]byte, 0, 1024)
		var t uint8
		var name string
		err = rows.Scan(&data, &t, &name)
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		err = rows.Close()
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		if t != FILE {
			myerror.Raise404(ctx, fmt.Errorf("类型是%d,不是file", t))
			return
		}
		ext := path.Ext(name)
		v, ok := CTL[ext]
		if ok {
			ctx.Data(200, v, data)
			return
		}
		ctx.Data(200, "application/octet-stream", data)
	}
}

func RootDir(ctx *gin.Context) {
	var rows *sql.Rows
	defer func() {
		if rows != nil {
			err := rows.Close()
			if err != nil {
				myerror.Raise500(ctx, err)
			}
		}
	}()
	userId, err := users.GetUser(ctx)
	if err != nil {
		return
	}
	rows, err = DB.Query("select id from file where type=2 and owner=?", userId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if !rows.Next() {
		_, err = DB.Exec("insert into file(file_name,parent_dir, type, data, owner, size) VALUES ('root',null,2,'',?,0)", userId)
		myerror.WriteWarning(fmt.Sprintf("用户%d没有root目录", userId))
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		return
	}
	var rootDirId uint64
	err = rows.Scan(&rootDirId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	err = rows.Close()
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	ctx.Redirect(302, "/netdisk/"+strconv.FormatUint(rootDirId, 10))
}

func ParentDir(ctx *gin.Context) {
	var rows *sql.Rows
	defer func() {
		if rows != nil {
			err := rows.Close()
			if err != nil {
				myerror.Raise500(ctx, err)
			}
		}
	}()
	dirId := ctx.Param("id")
	rows, err := DB.Query("select parent_dir from file where id=?", dirId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if !rows.Next() {
		myerror.Raise500(ctx, fmt.Errorf("没有这个父目录"))
		return
	}
	var pd uint64
	err = rows.Scan(&pd)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	ctx.Redirect(302, "/netdisk/"+strconv.FormatUint(pd, 10))
}

func CreateFile(ctx *gin.Context) {
	var tx *sql.Tx
	var warning string
	var err error
	defer func() {
		tx.Rollback()
	}()
	if ctx.Request.Method == "POST" {
		tx, err = DB.Begin()
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		pd, ok := ctx.GetPostForm("parent_dir")
		if !ok {
			myerror.Raise500(ctx, fmt.Errorf("没有表单参数parent_dir"))
			return
		}
		form, err := ctx.MultipartForm()
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		files := form.File["file"]
		for _, file := range files {
			if file.Size > 1_099_511_627_776 {
				warning = "单个文件大小不能超过1GB"
				continue
			}
			f, err := file.Open()
			if err != nil {
				myerror.Raise500(ctx, err)
				return
			}
			err = nil
			data := make([]byte, 0)
			for {
				d := make([]byte, 4096, 4096)
				if err != nil {
					break
				}
				var num int
				num, err = f.Read(d)
				data = append(data, d[0:num]...)
			}
			var fileTy FileType
			fileTy.Owner, err = users.GetUser(ctx)
			if err != nil {
				return
			}
			fileTy.Size = uint64(file.Size)
			fileTy.Type = 0
			fileTy.FileName = file.Filename
			fileTy.ParentDir, err = strconv.ParseUint(pd, 10, 64)
			if err != nil {
				myerror.Raise500(ctx, err)
				return
			}
			_, err = tx.Exec("insert into file(file_name, parent_dir, type, data, owner, size) values(?,?,?,?,?,?)", fileTy.FileName, fileTy.ParentDir, fileTy.Type, data, fileTy.Owner, fileTy.Size)
			if err != nil {
				myerror.Raise500(ctx, err)
				return
			}
			err = f.Close()
			if err != nil {
				myerror.Raise500(ctx, err)
				return
			}
		}
		next, ok := ctx.GetPostForm("next")
		if !ok {
			myerror.Raise500(ctx, fmt.Errorf("没有表单参数next"))
			return
		}
		err = tx.Commit()
		if err != nil {
			myerror.Raise500(ctx, err)
			return
		}
		// 注意:next一定要以“/”结尾
		if warning == "" {
			ctx.Redirect(302, next)
		} else {
			ctx.Redirect(302, next+"?warning="+warning)
		}
	}
}

func DeleteFile(ctx *gin.Context) {
	var rows *sql.Rows
	defer func() {
		if rows != nil {
			err := rows.Close()
			if err != nil {
				myerror.Raise500(ctx, err)
			}
		}
	}()
	userId, err := users.GetUser(ctx)
	if err != nil {
		return
	}
	dirId, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	rows, err = DB.Query("select parent_dir,type from file where id=?", dirId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if !rows.Next() {
		myerror.Raise500(ctx, fmt.Errorf("找不到目录"))
		return
	}
	var pd uint64
	var t uint8
	err = rows.Scan(&pd, &t)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	err = rows.Close()
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	if t == ROOT {
		myerror.Raise500(ctx, fmt.Errorf("不能删除root目录"))
		return
	}
	r, err := DB.Exec("delete from file where id=? and owner=?", dirId, userId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	rn, err := r.RowsAffected()
	if rn == 0 {
		myerror.Raise500(ctx, fmt.Errorf("没有这个目录或文件用来删除"))
		return
	}
	ctx.Redirect(302, "/netdisk/"+strconv.FormatUint(pd, 10))
}

func CreateDir(ctx *gin.Context) {
	dirName, ok := ctx.GetQuery("name")
	if !ok {
		myerror.Raise500(ctx, fmt.Errorf("没有name参数"))
		return
	}
	parentDir, ok := ctx.GetQuery("parent_dir")
	if !ok {
		myerror.Raise500(ctx, fmt.Errorf("没有parent_dir参数"))
		return
	}
	userId, err := users.GetUser(ctx)
	if err != nil {
		return
	}
	next, ok := ctx.GetQuery("next")
	if !ok {
		myerror.Raise500(ctx, fmt.Errorf("没有next参数"))
		return
	}
	_, err = DB.Exec("insert into file(file_name, parent_dir, type, owner, size) values(?,?,?,?,0)", dirName, parentDir, DIR, userId)
	if err != nil {
		myerror.Raise500(ctx, err)
		return
	}
	ctx.Redirect(302, next)
}
